﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace MirageTest
{
    class iTunesUtil
    {
        public static Dictionary<string, object> readDictionary(
           XmlTextReader reader)
        {
            // Check if we are currently at <dict>.
            if (reader.NodeType != XmlNodeType.Element || reader.Name != "dict")
            {
                throw new ArgumentException("Invalid node type or name");
            }

            // Create a new dictionary object.
            Dictionary<string, object> dict = new Dictionary<string, object>();

            // Read into it.
            while (true)
            {
                bool read = false;
                while ((read = reader.Read()) &&
                    reader.NodeType != XmlNodeType.Element && (
                    reader.NodeType != XmlNodeType.EndElement ||
                    reader.Name != "dict")) ;

                if (!read) break;

                // Check if we are done.
                if (reader.NodeType == XmlNodeType.EndElement)
                {
                    if (reader.Name != "dict")
                    {
                        throw new FormatException("Expected the </dict> node.");
                    }
                    return dict;
                }

                // Now we are at the <key> element.
                if (reader.Name != "key")
                {
                    throw new FormatException("Expected the <key> element.");
                }

                // Skip to the value.
                reader.Read();
                string key = reader.Value;

                // Skip to the next element;
                while (reader.Read() && reader.NodeType != XmlNodeType.Element) ;

                // Read the value.
                object val = readValue(reader);

                // At last add it to the dictionary.
                dict[key] = val;
            }
            return dict;
        }

        private static object readValue(XmlTextReader reader)
        {
            // Check if we are currently at an element.
            if (reader.NodeType != XmlNodeType.Element)
            {
                throw new ArgumentException("Expected an element.");
            }

            // Update the progress bar.

            object val;
            string type = reader.Name;
            switch (type)
            {
                case "string":
                    {
                        val = readString(reader);
                        break;
                    }
                case "integer":
                    {
                        string s = readString(reader);
                        val = long.Parse(s);
                        break;
                    }
                case "real":
                    {
                        string s = readString(reader);
                        val = double.Parse(s);
                        break;
                    }
                case "date":
                    {
                        string s = readString(reader);
                        // Remove the trailing 'Z'.
                        s = s.Substring(0, s.Length - 1);
                        // Parse.
                        val = DateTime.ParseExact(s, "s", null);
                        break;
                    }
                case "true":
                    {
                        reader.Read();
                        val = true;
                        break;
                    }
                case "false":
                    {
                        reader.Read();
                        val = false;
                        break;
                    }
                case "data":
                    {
                        string s = readString(reader);
                        val = Convert.FromBase64String(s);
                        break;
                    }
                case "dict":
                    {
                        val = readDictionary(reader);
                        break;
                    }
                case "array":
                    {
                        val = readArray(reader);
                        break;
                    }
                default:
                    {
                        throw new FormatException("Unknown element name: " + type);
                    }
            }
            return val;
        }

        private static string readString(XmlTextReader reader)
        {
            // Check if we are currently at an element.
            if (reader.NodeType != XmlNodeType.Element)
            {
                throw new ArgumentException("Expected an element.");
            }

            // Skip to the value.
            reader.Read();
            return reader.Value;
        }

        private static List<object> readArray(XmlTextReader reader)
        {
            // Check if we are currently at <dict>.
            if (reader.NodeType != XmlNodeType.Element || reader.Name != "array")
            {
                throw new ArgumentException("Invalid node type or name");
            }

            // Create a new array object.
            List<object> array = new List<object>();

            // Read into it.
            while (true)
            {
                bool read = false;
                while ((read = reader.Read()) &&
                    reader.NodeType != XmlNodeType.Element &&
                    reader.NodeType != XmlNodeType.EndElement) ;
                if (!read) break;

                // Check if we are done.
                if (reader.NodeType == XmlNodeType.EndElement)
                {
                    if (reader.Name != "array")
                    {
                        throw new FormatException("Expected the </array> node.");
                    }
                    return array;
                }

                // Read the value.
                object val = readValue(reader);

                // Add it to the array.
                array.Add(val);
            }
            return array;
        }

    }
}
